﻿// <copyright file="XMLDataFileParser.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008-2009 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2009-03-09</date>
// <summary>OpenWPFChart library. XML Data File Parser.</summary>
// <revision>$Id$</revision>

using System;
using System.Collections; // For IList
using System.Collections.Generic;
using System.Collections.ObjectModel; // For ObservableCollection
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Resources;
using OpenWPFChart.Helpers.XmlItemDataReader;
using OpenWPFChart.Parts;

namespace ColumnChartControlSample
{
	/// <summary>
	/// XML Data File Parser
	/// </summary>
	static class XMLDataFileParser
	{
		/// <summary>
		/// Parses the <paramref name="fileName"/> file.
		/// </summary>
		/// <param name="fileName">Name of the file.</param>
		/// <param name="xAxisLabel">The x axis label.</param>
		/// <returns><see cref="ItemData"/> collection from the <paramref name="fileName"/> file.</returns>
		/// <remarks>
		/// See XML data format description in SampleDataFiles/XmlFileFormat.txt. Details specific for
		/// this project see in the project Readme.txt file.
		/// </remarks>
		public static ObservableCollection<ItemData> Parse(string fileName, out string xAxisLabel)
		{
			xAxisLabel = null;

			// XML Data Mapping resource name.
			const string mappingResourceName = "xmldatamappings.xml";
			// Get xml Data Mapping stream from the "mappingResourceName" resource.
			Assembly assembly = Assembly.GetAssembly(typeof(XMLDataFileParser));
			ResourceManager rm = new ResourceManager(assembly.GetName().Name + ".g", assembly);
			IEnumerable<DataPointSeries> seriesCollection = null;
			using (TextReader mappingReader = new StreamReader(rm.GetStream(mappingResourceName)))
			using (TextReader dataReader = new StreamReader(File.OpenRead(fileName)))
			{
				seriesCollection = Loader.Load(dataReader, mappingReader);
			}

			ObservableCollection<ItemData> items = new ObservableCollection<ItemData>();
			foreach (DataPointSeries series in seriesCollection)
			{
				if (!string.IsNullOrEmpty(series.ItemType) && series.ItemType != "SampledCurve"
					&& series.ItemType != "ScatteredPoints")
					continue;

				xAxisLabel = series.Dimensions[0].Name;
				Type xBaseType = series.Dimensions[0].ClrType, yBaseType = series.Dimensions[1].ClrType;

				// Get OpenWPFChart.Parts.DataPoint<,> constructor.
				ConstructorInfo pointCtorInfo = GetTypedPointConstructorInfo(xBaseType, yBaseType);
				// Create OpenWPFChart.Parts.DataPoint<,> collection.
				IList points = CreateTypedPointCollectionInstance(xBaseType, yBaseType);
				// Fill point collection.
				foreach (object[] pt in series.GetPoints())
				{
					object typedDataPoint = pointCtorInfo.Invoke(pt);
					points.Add(typedDataPoint);
				}

				// Create ItemData object.
				string typeName = null;
				if (series.ItemType == "ScatteredPoints")
					typeName = string.Format("OpenWPFChart.Parts.ScatteredPointsData`2[{0},{1}],OpenWPFChart.Parts"
						, xBaseType.FullName, yBaseType.FullName);
				else
					typeName = string.Format("OpenWPFChart.Parts.SampledCurveData`2[{0},{1}],OpenWPFChart.Parts"
						, xBaseType.FullName, yBaseType.FullName);
				Type type = Type.GetType(typeName);
				ConstructorInfo ctorInfo = type.GetConstructor(new Type[0]);
				ItemData itemData = ctorInfo.Invoke(null) as ItemData;
				itemData.ItemName = series.Dimensions[1].Name;
				PropertyInfo pointsInfo = type.GetProperty("Points");
				pointsInfo.SetValue(itemData, points, null);

				items.Add(itemData);
			}
			return items;
		}

		/// <summary>
		/// Gets OpenWPFChart.Parts.DataPoint<,> constructor.
		/// </summary>
		/// <param name="xBaseType">Base x Type.</param>
		/// <param name="yBaseType">Base y Type.</param>
		/// <returns></returns>
		static ConstructorInfo GetTypedPointConstructorInfo(Type xBaseType, Type yBaseType)
		{
			string typeName = string.Format("OpenWPFChart.Parts.DataPoint`2[{0},{1}],OpenWPFChart.Parts"
				, xBaseType.FullName, yBaseType.FullName);
			Type type = Type.GetType(typeName);
			Debug.Assert(type != null, "type != null", "Typed Point type not found.");
			return type.GetConstructor(new Type[] { xBaseType, yBaseType });
		}

		/// <summary>
		/// Creates the Typed point collection.
		/// </summary>
		/// <param name="xBaseType">Base x Type.</param>
		/// <param name="yBaseType">Base y Type.</param>
		/// <returns></returns>
		static IList CreateTypedPointCollectionInstance(Type xBaseType, Type yBaseType)
		{
			string typeName = string.Format("System.Collections.Generic.List`1"
				+ "[[OpenWPFChart.Parts.DataPoint`2[{0},{1}],OpenWPFChart.Parts]]"
				, xBaseType.FullName, yBaseType.FullName);
			Type type = Type.GetType(typeName, true);
			Debug.Assert(type != null, "type != null", "Typed Point Collection type not found.");
			ConstructorInfo ctorInfo = type.GetConstructor(new Type[0]);
			Debug.Assert(ctorInfo != null, "ctorInfo != null");
			return ctorInfo.Invoke(null) as IList;
		}
	}
}
